/**
 *  This program is free software; you can redistribute it and/or modify it under 
 *  the terms of the GNU General Public License as published by the Free Software 
 *  Foundation; either version 3 of the License, or (at your option) any later 
 *  version.
 *  You should have received a copy of the GNU General Public License along with 
 *  this program; if not, see <http://www.gnu.org/licenses/>. 
 *  Use this application at your own risk.
 *
 *  Copyright (c) 2009 by Harald Mueller and Seth Lemons.
 */
 
package jasongong.tether.usb;

import jasongong.tether.usb.R;
import jasongong.tether.usb.data.ClientData;
import jasongong.tether.usb.system.CoreTask;
import jasongong.tether.usb.system.WebserviceTask;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Date;
//import java.util.Enumeration;
//import java.util.Hashtable;
import java.util.Properties;

import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;

public class TetherApplication extends Application {
	/////////////////////////////////////////////////////// 
	// add new variable definition ported from TetherSettings and ConnectivityManager
	///////////////////////////////////////////////////////
    private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
    String[] mUsbRegexs;
    public ArrayList mUsbIfaces;
    private String[] mWifiRegexs;
    // TODO: it is hard copied from source code of ConnectivityManager, better use reflection
    public static final int TETHER_ERROR_NO_ERROR           = 0;
    public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
    public static final int TETHER_ERROR_SERVICE_UNAVAIL    = 2;
    public static final int TETHER_ERROR_UNSUPPORTED        = 3;
    public static final int TETHER_ERROR_UNAVAIL_IFACE      = 4;
    public static final int TETHER_ERROR_MASTER_ERROR       = 5;
    public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
    public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
    public static final int TETHER_ERROR_ENABLE_NAT_ERROR     = 8;
    public static final int TETHER_ERROR_DISABLE_NAT_ERROR    = 9;
    public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;

	public static final String MSG_TAG = "TETHER -> TetherApplication";
	
	// StartUp-Check perfomed
	public boolean startupCheckPerformed = false;
	
	// Client-Connect-Thread
	/*private Thread clientConnectThread = null;
	private static final int CLIENT_CONNECT_AUTHORIZED = 1;
	*/
	// Data counters
	private Thread trafficCounterThread = null;

	// DNS-Server-Update Thread
	
	private Thread dnsUpdateThread = null;
	
	private Thread ipConfigureThread = null;
	
	
	// WifiManager
	public String tetherNetworkDevice = "";
	
	// PowerManagement
	private PowerManager powerManager = null;
	private PowerManager.WakeLock wakeLock = null;

	// Preferences
	public SharedPreferences settings = null;
	public SharedPreferences.Editor preferenceEditor = null;
	
    // Notification
	public NotificationManager notificationManager;
	private Notification notification;
	
	// Intents
	private PendingIntent mainIntent;
	
	/*
	 * CoreTask
	 */
	public CoreTask coretask = null;
	
	// WebserviceTask
	public WebserviceTask webserviceTask = null;
	
	//USB interface tetherable
	public String usbIface;
	public int tetherStarted;
	public int tetherStopped;
	
	// Client notification
	// not needed for reverse tethering for the time being.
	// jason 15April2012
	//Notification clientConnectNotification =  null;
	//String connectedMac = null;
	
	// Update Url
	// need to update this one jason-9April2012
	private static final String APPLICATION_PROPERTIES_URL = "http://code.google.com/p/android-reverse-tethering/svn/download/update/all/application.properties";
	private static final String APPLICATION_DOWNLOAD_URL = "http://code.google.com/p/android-reverse-tethering/files/";
	
	@Override
	public void onCreate() {
		Log.d(MSG_TAG, "Calling onCreate()");
		
		//create CoreTask
	    this.coretask = new CoreTask();
		this.coretask.setPath(this.getApplicationContext().getFilesDir().getParent());
		// set DATA_FILE_PATH in corretask
        Log.d(MSG_TAG, "Current directory is "+this.getApplicationContext().getFilesDir().getParent());

		//create WebserviceTask
		this.webserviceTask = new WebserviceTask();
		
        // Check Homedir, or create it
        this.checkDirs(); 
        
        // Preferences
		this.settings = PreferenceManager.getDefaultSharedPreferences(this);
		
        // preferenceEditor
        this.preferenceEditor = settings.edit();

        // Powermanagement
        powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "TETHER_WAKE_LOCK");
		
        // init notificationManager
        this.notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
    	this.notification = new Notification(R.drawable.start_notification, "Wired Tether", System.currentTimeMillis());
    	this.mainIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
    	
    	//TetherStarted initialized as -1
    	tetherStarted = -1;
    	tetherStopped = -1;
    	
    	//TODO clean up the tether.log file  
    	this.coretask.clearLog();
    	// Client notification
 	   	//this.clientConnectNotification = new Notification(R.drawable.connected, "Wired Tether", 0);
	///////////////////////////////////////////////////////////////////////////////////////
    /// port from android sdk	
	///////////////////////////////////////////////////////////////////////////////////////
    	ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    
    	Method getTetherableUsbRegexsLocal = null;
		try {
			getTetherableUsbRegexsLocal = cm.getClass().getMethod("getTetherableUsbRegexs");
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			Log.d(MSG_TAG, "cannot get method of getTetherableUsbRegexs, security exception");
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			Log.d(MSG_TAG, "cannot get method of getTetherableUsbRegexs, no such method exception");
			e.printStackTrace();
		}
    	try {
			mUsbRegexs = (String [])getTetherableUsbRegexsLocal.invoke(cm);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void onTerminate() { //as per latest android doc, the onTerminate only works in emulator and never be called in production devices.
		Log.d(MSG_TAG, "Calling onTerminate()");
    	// Stopping Tether
		this.stopTether();
		// Remove all notifications
		this.notificationManager.cancelAll();
	}

	// Start/Stop Tethering
    public int startTether() {
    	/*
    	 * ReturnCodes:
    	 *    0 = All OK, Service started
    	 *    1 = Mobile-Data-Connection not established (not used at the moment)
    	 *    2 = Fatal error 
    	 */
    	
    	/*boolean connected = this.mobileNetworkActivated();
        if (connected == false) {
        	// Go ahead even if there is no active mobile-network
        	 return 1;
        }*/
    	// Reset Client-Connect-Mac
    	//this.connectedMac = null;
        
       
    	ConnectivityManager cm =
            (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    	Method getTetherableIfacesLocal = null;
		try {
			getTetherableIfacesLocal = cm.getClass().getMethod("getTetherableIfaces");
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.d(MSG_TAG, "getTetherableIfaces got security exception ...");
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
    	String[] available = null;
    	try {
			available = (String [])getTetherableIfacesLocal.invoke(cm);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	usbIface = findIface(available, mUsbRegexs);
    	
    	if (usbIface == null) {
           // updateState();
    		//MainActivity.currentInstance.openNoUSBIfaceDialog(); cause bug, cannot change UI inside child thread
    		Message msg = Message.obtain();
        	msg.what = MainActivity.MESSAGE_NO_USB_INTERFACE;
        	msg.obj = "No tetherable usb inteface found ...";
        	MainActivity.currentInstance.viewUpdateHandler.sendMessage(msg);
        }
    	
 
    	// Starting service
        //if (this.coretask.runRootCommand(this.coretask.DATA_FILE_PATH+"/bin/tether start")) {
           	Method tetherLocal = null; 
    	 try {
 			tetherLocal = cm.getClass().getMethod("tether", String.class);
 		} catch (SecurityException e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();
			Log.d(MSG_TAG, "tether method got security exception ...");
 		} catch (NoSuchMethodException e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();
 		}	
    		
		//Log.d(MSG_TAG, "getTetheredIfaces got security exception ...");
 		
		try {
			tetherStarted = (Integer)tetherLocal.invoke(cm, usbIface);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		Log.d(MSG_TAG, "tetherlocal returned value is: "+ tetherStarted);
    	//has to use Integer
        if (tetherStarted == TETHER_ERROR_NO_ERROR) {	
        	//clientConnectEnable is mainly a separate thread to handle the dhcp leasing
        	//this is not required for reverse tethering jason-12Apri2012
        	//this.clientConnectEnable(true);
        	
    		this.trafficCounterEnable(true);
    		
			// Update resolv.conf-file
    		//Move to after starttether, because android internal tether will set its own DNS. 2012-July-12 Jason GONG
    		String dns[] = this.coretask.updateResolvConf();
			
    		this.dnsUpdateEnable(dns, true);
			
    		String network[] = this.coretask.readLanConf();
    		
        	this.ipConfigureEnable(network,true);
			
        	//debug here
        	this.coretask.appendLog("tethering started ...");
        	
			// Acquire Wakelock
			this.acquireWakeLock();
			
			//indicate the tether_stop is not valid
			this.tetherStopped = -1;
			
    		return 0;
    	}
    	return 2;
    }
    
    public boolean stopTether() {
    	this.releaseWakeLock();
    	//not needed for reverse tethering Jason 14Apri2012
    	//this.clientConnectEnable(false);
       	ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    	Method getTetheredIfacesLocal = null;
		try {
			getTetheredIfacesLocal = cm.getClass().getMethod("getTetheredIfaces");
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
				String[] tethered = null;
		try {
			tethered = (String [])getTetheredIfacesLocal.invoke(cm);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	usbIface = findIface(tethered, mUsbRegexs);
        if (usbIface == null) {
            //TO DO : return with a pop up message 
        	//MainActivity.currentInstance.openNoUSBIfaceDialog();
        	Message msg = Message.obtain();
        	msg.what = MainActivity.MESSAGE_NO_USB_INTERFACE;
        	msg.obj = "No tetherable usb inteface found ...";
        	MainActivity.currentInstance.viewUpdateHandler.sendMessage(msg);
            
        }
        Method untetherLocal = null;
        try {
			untetherLocal = cm.getClass().getMethod("untether", String.class);
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
		try {
			tetherStopped = (Integer)untetherLocal.invoke(cm, usbIface);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		if (tetherStopped == TETHER_ERROR_NO_ERROR) {
			this.notificationManager.cancelAll();
			this.trafficCounterEnable(false);
			this.dnsUpdateEnable(false);
		   	this.ipConfigureEnable(false);
		   	this.tetherStarted = -1;
			return true;
		}
		return false;
    }
    
    
    public String findIface(String[] ifaces, String[] regexes) {
        for (String iface : ifaces) {
            for (String regex : regexes) {
                if (iface.matches(regex)) {
                    return iface;
                }
            }
        }
        return null;
    }	
    public boolean restartTether() {
    	return this.restartTether(0, 0);
    }
    
    public boolean restartTether(int tetherModeToStop, int tetherModeToStart) {
    	/* TetherModes:
    	 * 		0 =  wifi
    	 * 		1 =  bluetooth
    	 */
    	//String command;
    	//boolean stopped = false;
    	//command = this.coretask.DATA_FILE_PATH+"/bin/tether stop";
		//stopped = this.coretask.runRootCommand(command);    	
    	//this.clientConnectEnable(false);
     	ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    	Method getTetheredIfacesLocal = null;
		try {
			getTetheredIfacesLocal = cm.getClass().getMethod("getTetheredIfaces");
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
		
		String[] tethered = null;
		try {
			tethered = (String [])getTetheredIfacesLocal.invoke(cm);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	String usbIface = findIface(tethered, mUsbRegexs);
        if (usbIface == null) {
            //TO DO : return with a pop up message
        	//MainActivity.currentInstance.openNoUSBIfaceDialog();
        	Message msg = Message.obtain();
        	msg.what = MainActivity.MESSAGE_NO_USB_INTERFACE;
        	msg.obj = "No tetherable usb inteface found ...";
        	MainActivity.currentInstance.viewUpdateHandler.sendMessage(msg);
            
        }
        Method untetherLocal = null;
        try {
			untetherLocal = cm.getClass().getMethod("untether", String.class);
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
		try {
			tetherStopped = (Integer)untetherLocal.invoke(cm, usbIface);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		boolean stopped = false;
		
		if (tetherStopped == TETHER_ERROR_NO_ERROR) {
			stopped = true;
			this.tetherStarted = -1;
		}
		
		
		
		//this.clientConnectEnable(false);
    	if (stopped != true) {
    		Log.d(MSG_TAG, "Couldn't stop tethering.");
    		return false;
    	}
    	//command = this.coretask.DATA_FILE_PATH+"/bin/tether start";
    	Method getTetherableIfacesLocal = null;
		try {
			getTetherableIfacesLocal = cm.getClass().getMethod("getTetheredIfaces");
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
    	String[] available = null;
    	try {
			available = (String [])getTetherableIfacesLocal.invoke(cm);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	usbIface = findIface(available, mUsbRegexs);
    	
    	if (usbIface == null) {
           // updateState();
            //pop up a message window with "no usb interface";
    		//MainActivity.currentInstance.openNoUSBIfaceDialog();
    		Message msg = Message.obtain();
        	msg.what = MainActivity.MESSAGE_NO_USB_INTERFACE;
        	msg.obj = "No tetherable usb inteface found ...";
        	MainActivity.currentInstance.viewUpdateHandler.sendMessage(msg);
    		return false;
        }
    	
    	// Starting service
        //if (this.coretask.runRootCommand(this.coretask.DATA_FILE_PATH+"/bin/tether start")) {
    	Method tetherLocal = null; 
    	 try {
 			tetherLocal = cm.getClass().getMethod("tether", String.class);
 		} catch (SecurityException e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();
 		} catch (NoSuchMethodException e) {
 			// TODO Auto-generated catch block
 			e.printStackTrace();
 		}	
 		
		try {
			tetherStarted = (Integer)tetherLocal.invoke(cm, usbIface);
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
 		
 		
    	if (tetherStarted == TETHER_ERROR_NO_ERROR) {
    	//if (this.coretask.runRootCommand(command)) {
    		//this.clientConnectEnable(true);
    		Log.d(MSG_TAG, "Stop tethering successfully.");
    		this.tetherStopped = -1;
    	}
    	else {
    		Log.d(MSG_TAG, "Couldn't stop tethering.");
    		return false;
    	}
    	return true;
    }
     
 /*   public boolean isTetheringSupported() {
        //enforceTetherAccessPermission();
        int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
        boolean tetherEnabledInSettings = (Settings.Secure.getInt(mContext.getContentResolver(),
                Settings.Secure.TETHER_SUPPORTED, defaultVal) != 0);
        return tetherEnabledInSettings && mTetheringConfigValid;
    }
   */    
    
    /*
     * this function dumps the securesetting to a txt file. If success, return true, otherwise false
     * do not use new thread to run it now. 
     */
    public boolean dumpSecureSettings(){
		String dumpSecureSettings = "echo \'.dump secure\' | sqlite3 " + this.coretask.SETTING_DB_PATH + "settings.db  > " + this.coretask.DATA_FILE_PATH + "/setting.txt";
		Log.d(MSG_TAG, "command for dumping the securesetting is: " + dumpSecureSettings);
		if(this.coretask.runRootCommand(dumpSecureSettings)==false){
			Log.e(MSG_TAG, "Unable to dump the secure setting to " + this.coretask.DATA_FILE_PATH+"/settings" +
					".txt");
			return false;
		} 
    	return true;
    }
 
    public boolean dumpSecureSettingsMaxID(){
		String dumpSecureSettingsMaxId = "sqlite3 "+this.coretask.SETTING_DB_PATH +"settings.db \'select max(_id) from secure\'"+  " > " + this.coretask.DATA_FILE_PATH + "/maxid_exit.txt";;
		Log.d(MSG_TAG, "command for dumping the max id is: " + dumpSecureSettingsMaxId);
		if(this.coretask.runRootCommand(dumpSecureSettingsMaxId)==false){
			Log.e(MSG_TAG, "Unable to dump the secure setting maxid to" + this.coretask.DATA_FILE_PATH+"maxid.txt");
			return false;
		} 
    	return true;
    }   
    
    // gets user preference on whether wakelock should be disabled during tethering
    public boolean isWakeLockDisabled(){
		return this.settings.getBoolean("wakelockpref", false);
	} 
	
    // gets user preference on whether sync should be disabled during tethering
    public boolean isSyncDisabled(){
		return this.settings.getBoolean("syncpref", false);
	}
    
    // gets user preference on whether sync should be disabled during tethering
    public boolean isUpdatecDisabled(){
		return this.settings.getBoolean("updatepref", false);
	}
    
    // get preferences on whether donate-dialog should be displayed
    public boolean showDonationDialog() {
    	return this.settings.getBoolean("donatepref", true);
    }

    // WakeLock
	public void releaseWakeLock() {
		try {
			if(this.wakeLock != null && this.wakeLock.isHeld()) {
				Log.d(MSG_TAG, "Trying to release WakeLock NOW!");
				this.wakeLock.release();
			}
		} catch (Exception ex) {
			Log.d(MSG_TAG, "Ups ... an exception happend while trying to release WakeLock - Here is what I know: "+ex.getMessage());
		}
	}
    
	public void acquireWakeLock() {
		try {
			if (this.isWakeLockDisabled() == false) {
				Log.d(MSG_TAG, "Trying to acquire WakeLock NOW!");
				this.wakeLock.acquire();
			}
		} catch (Exception ex) {
			Log.d(MSG_TAG, "Ups ... an exception happend while trying to acquire WakeLock - Here is what I know: "+ex.getMessage());
		}
	}
    
    // Notification
	// can still keep the start notification for reverse tethering.
    public void showStartNotification() {
		notification.flags = Notification.FLAG_ONGOING_EVENT;
    	notification.setLatestEventInfo(this, "Wired Tether", "Tethering is currently running ...", this.mainIntent);
    	this.notificationManager.notify(-1, this.notification);
    }
    //since the clientConnect thread is no longer needed, we
    //can remove the handler as well. jason 14Apri2012
    /*Handler clientConnectHandler = new Handler() {
 	   public void handleMessage(Message msg) {
 		   ClientData clientData = (ClientData)msg.obj;
 		   if (TetherApplication.this.connectedMac == null || TetherApplication.this.connectedMac.equals(clientData.getMacAddress()) == false) {
 			   TetherApplication.this.connectedMac = clientData.getMacAddress();
 			   TetherApplication.this.showClientConnectNotification(clientData);
 		   }
 	   }
    };
    */
    /*public void showClientConnectNotification(ClientData clientData) {
		Log.d(MSG_TAG, "New client connected ==> "+clientData.getClientName()+" - "+clientData.getMacAddress());
 	   	clientConnectNotification.tickerText = clientData.getClientName()+" ("+clientData.getMacAddress()+")";
 	   	if (!this.settings.getString("notifyring", "").equals(""))
 	   		clientConnectNotification.sound = Uri.parse(this.settings.getString("notifyring", ""));

 	   	if(this.settings.getBoolean("notifyvibrate", true))
 	   		clientConnectNotification.vibrate = new long[] {100, 200, 100, 200};

 	   	clientConnectNotification.setLatestEventInfo(this, "Wired Tether", clientData.getClientName()+" ("+clientData.getMacAddress()+") connected ...", this.mainIntent);
 	   	clientConnectNotification.flags = Notification.FLAG_AUTO_CANCEL;
 	   	this.notificationManager.notify(1, clientConnectNotification);
    } 
    */
	/*
    public void recoverConfig() {                      //never been called ?
    	// updating lan-settings
    	String lanconfig = this.settings.getString("lannetworkpref", "172.20.23.252/30");
    	this.coretask.writeLanConf(lanconfig);
    	
    	this.displayToastMessage("Configuration recovered.");
    }*/
   
    public boolean binariesExists() {
    	File file_ifconfig = new File(this.coretask.DATA_FILE_PATH+"/bin/ifconfig");
    	File file_route = new File(this.coretask.DATA_FILE_PATH+"/bin/route");
    	return (file_ifconfig.exists() && file_route.exists());
    }
    
    Handler displayMessageHandler = new Handler(){
        public void handleMessage(Message msg) {
       		if (msg.obj != null) {
       			TetherApplication.this.displayToastMessage((String)msg.obj);
       		}
        	super.handleMessage(msg);
        }
    };
    
    
    /*
     * TODO: 	To check if the ifconfig and lanconf is really needed
	 * Summary:	Copy the necessary command files to device folder.  
	 * DATE:	8-July-20
     */
    public void installFiles() {
    	new Thread(new Runnable(){
			public void run(){
				String message = null;
		    	// ifconfig
		    	if (message == null) {
			    	message = TetherApplication.this.copyBinary(TetherApplication.this.coretask.DATA_FILE_PATH+"/bin/ifconfig", R.raw.ifconfig);
		    	}
		    	//add route command from busybox to serve the setup gw purpose.
		    	if (message == null) {
		    		message = TetherApplication.this.copyBinary(TetherApplication.this.coretask.DATA_FILE_PATH+"/bin/route", R.raw.route);
		    	}
				try {
		    		TetherApplication.this.coretask.chmodBin();
				} catch (Exception e) {
					message = "Unable to change permission on binary files!";
				}
				// version
				if (message == null) {
					TetherApplication.this.copyBinary(TetherApplication.this.coretask.DATA_FILE_PATH+"/conf/version", R.raw.version);
				}				
				if (message == null) {
			    	message = "Binaries and config-files installed!";
				}
				
				// Removing ols lan-config-file
				File lanConfFile = new File(TetherApplication.this.coretask.DATA_FILE_PATH+"/conf/lan_network.conf");
				if (lanConfFile.exists()) {
					lanConfFile.delete();
				}
				
				// Sending message
				Message msg = new Message();
				msg.obj = message;
				TetherApplication.this.displayMessageHandler.sendMessage(msg);
			}
		}).start();
    }
    
    public void checkForUpdate() {
    	if (this.isUpdatecDisabled()) {
    		Log.d(MSG_TAG, "Update-checks are disabled!");	
    		return;
    	}
    	new Thread(new Runnable(){
			public void run(){
				Looper.prepare();
				// Getting Properties
				Properties updateProperties = TetherApplication.this.webserviceTask.queryForProperty(APPLICATION_PROPERTIES_URL);//need to update the link
				if (updateProperties != null && updateProperties.containsKey("versionCode") && updateProperties.containsKey("fileName")) {
					int availableVersion = Integer.parseInt(updateProperties.getProperty("versionCode"));
					int installedVersion = TetherApplication.this.getVersionNumber();
					String fileName = updateProperties.getProperty("fileName");
					if (availableVersion != installedVersion) {
						Log.d(MSG_TAG, "Installed version '"+installedVersion+"' and available version '"+availableVersion+"' do not match!");
						MainActivity.currentInstance.openUpdateDialog(APPLICATION_DOWNLOAD_URL+fileName, fileName);
					}
				}
				Looper.loop();
			}
    	}).start();
    }
   
    public void downloadUpdate(final String downloadFileUrl, final String fileName) {
    	new Thread(new Runnable(){
			public void run(){
				Message msg = Message.obtain();
            	msg.what = MainActivity.MESSAGE_DOWNLOAD_STARTING;
            	msg.obj = "Downloading update...";
            	MainActivity.currentInstance.viewUpdateHandler.sendMessage(msg);
				TetherApplication.this.webserviceTask.downloadUpdateFile(downloadFileUrl, fileName);
				Intent intent = new Intent(Intent.ACTION_VIEW); 
			    intent.setDataAndType(android.net.Uri.fromFile(new File(WebserviceTask.DOWNLOAD_FILEPATH+"/"+fileName)),"application/vnd.android.package-archive"); 
			    MainActivity.currentInstance.startActivity(intent);
			}
    	}).start();
    }
    
    private String copyBinary(String filename, int resource) {
    	File outFile = new File(filename);
    	Log.d(MSG_TAG, "Copying file '"+filename+"' ...");
    	InputStream is = this.getResources().openRawResource(resource);
    	byte buf[]=new byte[1024];
        int len;
        try {
        	OutputStream out = new FileOutputStream(outFile);
        	while((len = is.read(buf))>0) {
				out.write(buf,0,len);
			}
        	out.close();
        	is.close();
		} catch (IOException e) {
			return "Couldn't install file - "+filename+"!";
		}
		return null;
    }
    
    private void checkDirs() {
    	File dir = new File(this.coretask.DATA_FILE_PATH);
    	if (dir.exists() == false) {
    			this.displayToastMessage("Application data-dir does not exist!");
    	}
    	else {
    		String[] dirs = { "/bin", "/var", "/conf", "/library" };
    		for (String dirname : dirs) {
    			dir = new File(this.coretask.DATA_FILE_PATH + dirname);
    	    	if (dir.exists() == false) {
    	    		if (!dir.mkdir()) {
    	    			this.displayToastMessage("Couldn't create " + dirname + " directory!");
    	    		}
    	    	}
    	    	else {
    	    		Log.d(MSG_TAG, "Directory '"+dir.getAbsolutePath()+"' already exists!");
    	    	}
    		}
    	}
    }
 
    // Display Toast-Message
	public void displayToastMessage(String message) {
		Toast.makeText(this, message, Toast.LENGTH_LONG).show();
	}
    
    public int getVersionNumber() {
    	int version = -1;
        try {
            PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
            version = pi.versionCode;
        } catch (Exception e) {
            Log.e(MSG_TAG, "Package name not found", e);
        }
        return version;
    }
    
    public String getVersionName() {
    	String version = "?";
        try {
            PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
            version = pi.versionName;
        } catch (Exception e) {
            Log.e(MSG_TAG, "Package name not found", e);
        }
        return version;
    }
    
 
   
    //it is not really necessary, but won't do any bad if kept, just update the resolv.conf file inside apps directory
    //if it is really needed 23Jun2012 jason GONG
	
    public void dnsUpdateEnable(boolean enable) {
    	this.dnsUpdateEnable(null, enable);
    }
 
  	public void dnsUpdateEnable(String[] dns, boolean enable) {
   		if (enable == true) {
			if (this.dnsUpdateThread == null || this.dnsUpdateThread.isAlive() == false) {
				this.dnsUpdateThread = new Thread(new DnsUpdate(dns));
				this.dnsUpdateThread.start();
			}
   		} else {
	    	if (this.dnsUpdateThread != null)
	    		this.dnsUpdateThread.interrupt();
   		}
   	}
    
  	class DnsUpdate implements Runnable {

    	String[] dns;
    	
    	public DnsUpdate(String[] dns) {
    		this.dns = dns;
    	}
    	
		//@Override
		public void run() {
            while (!Thread.currentThread().isInterrupted()) {
            	String[] currentDns = TetherApplication.this.coretask.getCurrentDns();
            	if (this.dns == null || this.dns[0].equals(currentDns[0]) == false || this.dns[1].equals(currentDns[1]) == false) {
            		this.dns = TetherApplication.this.coretask.updateResolvConf();
            	}
                // Taking a nap
       			try {
    				Thread.sleep(2000);
    			} catch (InterruptedException e) {
    				Thread.currentThread().interrupt();
    			}
            }
		}
    }

    public void ipConfigureEnable(boolean enable) {
    	this.ipConfigureEnable(null, enable);
    }  	
  	
  	
  	public void ipConfigureEnable(String[] network, boolean enable) {
   		if (enable == true) {
			if (this.ipConfigureThread == null || this.ipConfigureThread.isAlive() == false) {
				this.ipConfigureThread = new Thread(new ipConfigure(network));
				this.ipConfigureThread.start();
			}
   		} else {
	    	if (this.ipConfigureThread != null)
	    		this.ipConfigureThread.interrupt();
   		}
   	}
    
  	class ipConfigure implements Runnable {

    	String[] network;
    	
    	public ipConfigure(String[] network) {
    		this.network = network;
    	}
    	
		//@Override
    	/*
    	 * cannot simply copy over the updateDNS function, it is ok to keep updating dns,
    	 * but the ifconfig only need setup once. 
    	 */
		public void run() {
            //while (!Thread.currentThread().isInterrupted()) {
            	//String[] currentDns = TetherApplication.this.coretask.getCurrentDns();//current means current system setting, not setting inside file.
            	//if (this.dns == null || this.dns[0].equals(currentDns[0]) == false || this.dns[1].equals(currentDns[1]) == false) {
            	  if(TetherApplication.this.coretask.ifConfigUpInterface(TetherApplication.this.usbIface)){ //ifconfig usb up command execution
            		  if(TetherApplication.this.coretask.ifConfigSetInterface(TetherApplication.this.usbIface, network)){
            			  if(TetherApplication.this.coretask.dumpDefaultGW())
            			  {
            				  
            				  String[] currentGW = TetherApplication.this.coretask.getCurrentGW();
            				  
            				  if ((currentGW[0].equals(network[1])==false)  || (currentGW[1].equals(usbIface)==false)){
            				  
            					  if(TetherApplication.this.coretask.ifConfigSetGW(TetherApplication.this.usbIface, network))
            					  {
            						  Log.d(MSG_TAG, "ifconfig setup success");
            				  
            					  }
            			  	  
            					  else
            					  {
            						  Log.d(MSG_TAG, "cannot set default gate way"); 
            					  }
            			       
            				  
            			  		}
            				  else 
            				  {
            					  Log.d(MSG_TAG, "existing gateway is already correct");
            				  }
            			   
            		       }
            			  else
            			  {
            				  Log.d(MSG_TAG, "cannot dump system gate way"); 
            			  }
            		  }	  
            		  else
            		  {
            			  Log.d(MSG_TAG, "cannot set ifconfig inteface");
            		  }	
            	    }
            	  else 
            	  {
            		  Log.d(MSG_TAG, "cannot up usb interface");
            	  }
            	//}

            }
		//}
    }
  	
  	
  	public void isTetherEnabled() {
 	


    }
  	
   	public void trafficCounterEnable(boolean enable) {
   		if (enable == true) {
			if (this.trafficCounterThread == null || this.trafficCounterThread.isAlive() == false) {
				this.trafficCounterThread = new Thread(new TrafficCounter());
				this.trafficCounterThread.start();
			}
   		} else {
	    	if (this.trafficCounterThread != null)
	    		this.trafficCounterThread.interrupt();
   		}
   	}
 
   	
	class TrafficCounter implements Runnable {
   		private static final int INTERVAL = 2;  // Sample rate in seconds.
   		long previousDownload;
   		long previousUpload;
   		long lastTimeChecked;
   		
   		//@Override
   		public void run() {
   			this.previousDownload = this.previousUpload = 0;
   			this.lastTimeChecked = new Date().getTime();
   			
   			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// nothing
			}
   			
	        long [] trafficCountAtStart = TetherApplication.this.coretask.getDataTraffic(
	        		TetherApplication.this.tetherNetworkDevice);
   			
   			while (!Thread.currentThread().isInterrupted()) {
		        // Check data count
		        long [] trafficCount = TetherApplication.this.coretask.getDataTraffic(
		        		TetherApplication.this.tetherNetworkDevice);
		        long currentTime = new Date().getTime();
		        float elapsedTime = (float) ((currentTime - this.lastTimeChecked) / 1000);
		        this.lastTimeChecked = currentTime;
		        DataCount datacount = new DataCount();
		        datacount.totalUpload = trafficCount[0]-trafficCountAtStart[0];
		        datacount.totalDownload = trafficCount[1]-trafficCountAtStart[1];
		        datacount.uploadRate = (long) ((datacount.totalUpload - this.previousUpload)*8/elapsedTime);
		        datacount.downloadRate = (long) ((datacount.totalDownload - this.previousDownload)*8/elapsedTime);
				Message message = Message.obtain();
				message.what = MainActivity.MESSAGE_TRAFFIC_COUNT;
				message.obj = datacount;
				MainActivity.currentInstance.viewUpdateHandler.sendMessage(message); 
				this.previousUpload = datacount.totalUpload;
				this.previousDownload = datacount.totalDownload;
                try {
                    Thread.sleep(INTERVAL * 1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
   			}
			Message message = Message.obtain();
			message.what = MainActivity.MESSAGE_TRAFFIC_END;
			MainActivity.currentInstance.viewUpdateHandler.sendMessage(message); 
   		}
   	}
   	
   	public class DataCount {
   		// Total data uploaded
   		public long totalUpload;
   		// Total data downloaded
   		public long totalDownload;
   		// Current upload rate
   		public long uploadRate;
   		// Current download rate
   		public long downloadRate;
   	}
}
